#include "LibUSBControl.h"

LibUSBControl::LibUSBControl()
{

}


static libusb_device_handle* g_usb_handle;
static libusb_context *ctx;
static libusb_interface_descriptor *describe;
static libusb_transfer *main_transfer;


/// 热插拔回调事件
void hotplugin(){
    while (1) {
        libusb_handle_events_completed(ctx, NULL);
        std::this_thread::sleep_for( std::chrono::milliseconds(1000) ) ;
    }
}


static int LIBUSB_CALL
usb_arrived_callback(struct libusb_context *_ctx, struct libusb_device *dev,
    libusb_hotplug_event event, void *userdata)
{
    struct libusb_device_handle *handle;
    struct libusb_device_descriptor desc;
    unsigned char buf[512];
    int rc;

    libusb_get_device_descriptor(dev, &desc);
//    printf("Add usb device: \n");
//    printf("\tCLASS(0x%x) SUBCLASS(0x%x) PROTOCOL(0x%x)\n",desc.bDeviceClass, desc.bDeviceSubClass, desc.bDeviceProtocol);
//    printf("\tVENDOR(0x%x) PRODUCT(0x%x)\n", desc.idVendor, desc.idProduct);


    if((desc.idVendor == user_device.idVendor) && (desc.idProduct == user_device.idProduct)){
        g_usb_handle = libusb_open_device_with_vid_pid(NULL, user_device.idVendor, user_device.idProduct);
        //user_device.usb_port = libusb_get_port_number(dev);

        int ret  = 0;

        struct libusb_config_descriptor *config;
        ret = libusb_get_config_descriptor(dev, 0, &config);

        if (LIBUSB_SUCCESS != ret) {
            printf("不能获取设备描述符\n");
        }
        user_device.outEndPoint = (config->interface->altsetting->endpoint->bEndpointAddress & 0x07);

        describe = const_cast<libusb_interface_descriptor*>(config->interface->altsetting);

#if ZXX_DEBUG
        printf("        端口号: %0x\n", libusb_get_device_address(dev));
        printf("        总线号: %0x\n", libusb_get_bus_number(dev));
        printf("        bDescriptorType: %d\n", config->interface->altsetting->bDescriptorType);
        printf("        bEndpointAddress: %02xh\n", config->interface->altsetting->endpoint->bEndpointAddress);
        printf("        传输类型:     %02xh\n", config->interface->altsetting->endpoint->bmAttributes);
        printf("        wMaxPacketSize:   %d\n", config->interface->altsetting->endpoint->wMaxPacketSize);
        printf("        传输速率:        %d ms\n", config->interface->altsetting->endpoint->bInterval);
#endif

        user_device.ms_Request = config->interface->altsetting->endpoint->bInterval;

        libusb_free_config_descriptor(config);

        if(g_usb_handle == NULL) {
            qWarning("\t 拒绝访问USB设备!\n");
            qWarning("\t 请USB检查驱动，权限!\n");
            user_device.isOpen= false;
            return -1;
        }else {
            user_device.isOpen= true;
            qDebug()<<"\t USB摇杆访问成功!";
        }
    }else {
        return 0;
        rc = libusb_open(dev, &handle);
        if (LIBUSB_SUCCESS != rc) {
            printf("Could not open USB device\n");
            return 0;
        }

        memset(buf, 0, sizeof(buf));
        rc = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, buf, sizeof(buf));
        if (rc < 0) {
            printf("Get Manufacturer failed\n");
        } else {
            printf("\tManufacturer: %s\n", buf);
        }

        memset(buf, 0, sizeof(buf));
        rc = libusb_get_string_descriptor_ascii(handle, desc.iProduct, buf, sizeof(buf));
        if (rc < 0) {
            printf("Get Product failed\n");
        } else {
            printf("\tProduct: %s\n", buf);
        }

        memset(buf, 0, sizeof(buf));
        rc = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, buf, sizeof(buf));
        if (rc < 0) {
            printf("Get SerialNumber failed\n");
        } else {
            printf("\tSerialNumber: %s\n", buf);
        }
        libusb_close(handle);
    }


    return 0;
}

static int LIBUSB_CALL
usb_left_callback(struct libusb_context *ctx, struct libusb_device *dev,
    libusb_hotplug_event event, void *userdata)
{
    struct libusb_device_descriptor desc;

    libusb_get_device_descriptor(dev, &desc);
    printf("Remove usb device: CLASS(0x%x) SUBCLASS(0x%x) iSerialNumber(0x%x)\n",
        desc.bDeviceClass, desc.bDeviceSubClass, desc.iSerialNumber);

    return 0;
}

int LibUSBControl::init_libusb(void)
{
    libusb_hotplug_callback_handle usb_arrived_handle;
    libusb_hotplug_callback_handle usb_left_handle;
    libusb_hotplug_flag flag = static_cast<libusb_hotplug_flag>(1);

    int rv = 0;

    rv = libusb_init(&ctx);
    rv = libusb_hotplug_register_callback(ctx, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
        flag, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
        LIBUSB_HOTPLUG_MATCH_ANY, usb_arrived_callback, NULL, &usb_arrived_handle);

    if (LIBUSB_SUCCESS != rv) {
        printf("注册USB热插拔失败! \n");
        libusb_exit(ctx);
        return EXIT_FAILURE;
    }
    rv = libusb_hotplug_register_callback(ctx, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
        flag, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
        LIBUSB_HOTPLUG_MATCH_ANY, usb_left_callback, NULL, &usb_left_handle);

    if (LIBUSB_SUCCESS != rv) {
        printf("注册USB热插拔失败! \n");
        libusb_exit(ctx);
        return EXIT_FAILURE;
    }


    libusb_hotplug_deregister_callback(ctx, usb_arrived_handle);
    libusb_hotplug_deregister_callback(ctx, usb_left_handle);


    auto th = new std::thread(hotplugin);
    th->detach();
    return rv;


}

/// 后台线程查询中断
void LibUSBControl::thread_callback()
{
    int length ,rv= 0;

    UC _readbuff[HID_PROTO_LEN];
    memset(_readbuff,0,sizeof(_readbuff));

   //读中断端点1,HID,此3D摇杆使用中断读取模式
    rv=libusb_interrupt_transfer(g_usb_handle,HID_ENDPOINT_IN,_readbuff,sizeof(_readbuff),&length,0);

    if((rv < 0)){
        qDebug()<<libusb_error_name(rv);
    }else {
        std::string recv;
        for(int i = 0;i < sizeof(_readbuff);i++){
            recv.push_back(UC(_readbuff[i]));
        }
        _sigUpdate(recv);
    }
}


int LibUSBControl::openDevice(int pid, int vid)
{
    user_device.idProduct = pid;
    user_device.idVendor =  vid ;

    //int ret ;
    int ret =  init_libusb();


    user_device.outEndPoint = 0x00;

    ret = libusb_kernel_driver_active(g_usb_handle, user_device.outEndPoint);

    if(ret == 1){
        /// 卸载驱动
        ret = libusb_detach_kernel_driver(g_usb_handle, user_device.outEndPoint);
        if(ret < 0){
            qDebug("libusb libusb_detach_kernel_driver_np #0: %s", libusb_error_name(ret));
        }
    }

    /// 为指定的设备申请接口,bInterfaceNumber
    if ((ret=libusb_claim_interface(g_usb_handle, user_device.outEndPoint)) < 0) {   //设置usb设备接口为0
        qDebug("error claiming interface #0: %s", libusb_error_name(ret));
    }


    /// 后台去查
    std::thread([this](){
        while (user_device.isOpen) {
            thread_callback();
            sleep_ms(user_device.ms_Request);
            //std::this_thread::sleep_for( std::chrono::milliseconds(user_device.ms_Request) ) ;
        }
    }).detach();


    return ret;
}


/// 不要在外部查询
bool LibUSBControl::ReadBuff()
{
    return false;
}

int LibUSBControl::OpenPort(int pid, int vid)
{
    int ret = openDevice(pid,vid);
    if(!(ret < 0))
        undo();


    return ret;
}

int LibUSBControl::CloseDevice()
{

    user_device.isOpen = false;

    libusb_close(g_usb_handle);

    libusb_release_interface(g_usb_handle,describe->bInterfaceNumber);

    libusb_exit(NULL);
}

int LibUSBControl::Send(const char *)
{

}

/// 从这里开始，下面的代码似乎无法正常工作
/*************************************************************************************************************/

/// 尝试注册recv回调，但是回调函数必须是静态的
/// 静态函数无法发送我的boost信号
/// so,i give up.
static  LIBUSB_CALL
void recv_callback(struct libusb_transfer *transfer)
{
    if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
           printf("callback transfer not completed!\n");
           return ;
    }
    qDebug()<<4396;

    std::string recv;
    for(int i = 0;i < sizeof(transfer->buffer);i++){
        recv.push_back(UC(transfer->buffer[i]));
    }
    qDebug()<<QByteArray::fromStdString(recv);
    //_sigUpdate(recv);
}


/// 异步回调方法：中断模式
/// 我尝试了使用自动回调的方法，可惜失败了，似乎只能手动的去查询中断读端点
void LibUSBControl::registerInterrupt()
{
    UC *ctl_buf = (unsigned char*) malloc(19 );
    memset(ctl_buf, 0, sizeof(ctl_buf));
    ///  申请transfer缓冲区
    main_transfer = libusb_alloc_transfer(0);
    libusb_fill_interrupt_transfer(main_transfer, g_usb_handle,0x81, ctl_buf,19, recv_callback, NULL, 0);
    main_transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER;
    int ret = libusb_submit_transfer(main_transfer);
    if(ret < 0){
         libusb_free_transfer(main_transfer);
         qDebug("libusb libusb_submit_transfer #0: %s", libusb_error_name(ret));
    }
}
